Как мы работаем со Stateful в Kubernetes: особенности и подводные камни

Всем привет! На связи Олег Сапрыкин, технический директор по инфраструктуре и тимлид DevOps-команд во «Фланте». В марте 2024 года мы с Андреем Радыгиным (ex-главный архитектор по внедрению Deckhouse) выступили на конференции DevOpsConf. Эта статья — текстовый вариант нашего доклада о развитии и нашем опыте использования Stateful в Kubernetes.

2327d57f253353166e18d7fe4dfb31a2.png

Мы рассказали, как оценивать Stateful-компонент, прежде чем запускать его в Kubernetes, показали нюансы работы с такими приложениями, а также поделились особенностями конфигурирования и опытом использования некоторых Stateful-операторов — ClickHouse, Redis, Kafka, PostgreSQL и MySQL.

В 2018 году на Highload++ были сделаны два фундаментальных прогноза:

  • Stateful будет развиваться в Kubernetes.

  • Stateful перейдёт под автоматическое управление операторами.

С тех пор прошло шесть лет: прогнозы сбылись, а мы получили много релевантного опыта, которым и хотим поделиться с вами. Прежде чем детально погружаться в обзор, кратко расскажем о предыстории Kubernetes и появлении в нём Stateful в контексте опыта «Фланта».

Как индустрия и мы вместе с ней пришли к Kubernetes и Stateful

В середине нулевых начала активно развиваться разработка на микросервисах. Это создало потребность в новой технологической базе, так как запускать микросервисы и управлять ими старыми методами было крайне неудобно. Решение проблемы нашли в контейнерах. Однако они же со временем породили новую: масштабы и число контейнеров росли. Поэтому понадобились новые инструменты для оркестрации. Параллельно с этим Google в 2003 году создали свой оркестратор рабочих нагрузок Borg, который использовал собственные закрытые технологии контейнеров (или чего-то похожего на них). В последующие годы появлялись другие решения по оркестрации контейнеров, например LXC или OpenVZ, однако ни одно из них не стало массовым. Ближе к 2013 году на сцену вышел Docker и представил инновационную экосистему, реализующую контейнеризацию. Google признали перспективность нового решения и на основе Docker-контейнеров создали наследника Borg — Kubernetes.

Появление Kubernetes не вызвало в сообществе особого энтузиазма: он казался чрезмерным, ненужным усложнением, поэтому его не спешили внедрять. Несмотря на это, было очевидно, что микросервисы, Docker-контейнеры, а далее и Kubernetes приносили явную пользу в разработке, несмотря на изначальный скепсис инженеров. Поэтому начиная примерно с 2014 года начался закономерный взлёт популярности контейнеризации и оркестраторов, и уже к 2018 году Kubernetes оформился как стандарт де-факто в индустрии. Согласно опросу CNCF, в 2020 году более 80% enterprise-пользователей активно использовали Kubernetes в своих окружениях.

Мы во «Фланте» были сторонниками Kubernetes с самого начала его существования. Мы поняли, что он «из коробки» способен облегчить нашу инженерную жизнь и дать хорошую инфраструктуру: масштабируемую, отказоустойчивую, с балансировкой нагрузки и мониторингом. Кроме того, мы нашли в Kubernetes избавление от сильнейшей боли всех инженеров — возможность унификации: он даёт единый интерфейс и API.

«Ранний» Kubernetes реализовывал сложные и гибкие механизмы доставки, умел распределять нагрузку и управлять множеством контейнеров, обеспечивал самовосстановление и гибкую утилизацию ресурсов. Однако тогда все эти задачи решались только для Stateless-сервисов.

Со временем у разработчиков появились запросы на динамические окружения, то есть такие, в которых под каждую ветку приложения создано множество копий — обычных dev-стендов, на которых можно тестировать софт. Поскольку у каждого приложения есть базы и очереди, в каждом таком окружении уже нужно было, по сути, реализовывать Stateful.

Параллельно также развивались Cloud Native Stateful-приложения, в которых вопросы отказоустойчивости и масштабирования были актуальны со времён появления самих облаков и стали ещё более важными с появлением трендов на мультизоны и кросс-ЦОДовые инсталляции.

Таким образом, потребность в реализации Stateful в Kubernetes возникла не только у нас. Просьбы сообщества были услышаны, и уже через 1,5–2 года после выхода Kubernetes, в версии 1.3, появился PetSet, который позже переименовали в StatefulSet. В 2019 году появились Custom Resource Definitions (CRD), которые позволяют воплощать разную гибкую логику управления приложениями, более сложную, чем просто управление набором экземпляров в деплойменте.

С историей разобрались — перейдём к более практическим моментам. Про Kubernetes бытует мнение, что достаточно задеплоить рядом со своим приложением YAML-файлы и всё заработает — раздумывать особо не надо. Когда дело касается Stateful, подумать всё-таки нужно, чтобы понять, надо ли вообще запускать этот компонент в Kubernetes.

Как оценить Stateful-компонент перед запуском в Kubernetes

Есть несколько критериев, чтобы оценить компонент, прежде чем запускать его в Kubernetes.

Производительность: хватит ли её, не упрёмся ли мы в сеть или диски.

Сохранность данных: тут речь скорее про персональные данные — сможем ли мы поместить базу данных туда, где находится контур с кластером.

Отказоустойчивость: может ли приложение/компонент/база масштабироваться, находиться в двух репликах и получится ли достичь нужного уровня SLA.

Бэкапы: зачастую в компании бэкапы делаются централизованно, поэтому, если запустить какой-то компонент в Kubernetes, не всегда возможно сделать его бэкап.

Целесообразность: например, если нужно просто задеплоить и использовать какую-то базу данных, необязательно гнаться за трендами — возможно, достаточно скриптов на Ansible. Другое дело, если необходимо воспроизводить это сотни и тысячи раз в каких-то dev-окружениях — тогда имеет смысл разобраться и перенести это в Kubernetes.

Как работает Stateful в Kubernetes

Прежде чем разбирать пример, освежим немного теорию.

Есть какой-то потребитель (по сути, это всегда под), которому нужен Volume, чтобы хранить данные. Для этого он отправляет PersistentVolumeClaim (PVC) в StorageClass (SC) и через этот StorageClass выписывает себе PersistentVolume (PV). Дальше Kubernetes просто идёт в Storage, выделяет там раздел и монтирует его к поду. После этого под читает и пишет данные с этого Volume. При этом, если, например, под перезапустится, данные никуда не денутся.

da43d4000a543aa00a356a65c01c79fb.png

Теперь представим, что у нас есть PostgreSQL, которую мы хотим разместить в кластере. Монтируем datadir, который использует PostgreSQL, в манифест StatefulSet.

kubectl get sts psql -o yaml
... 
 volumeMounts:
        - mountPath: /var/lib/postgresql
          name: pgdata
...

Воспроизводим это N раз на стендах разработки, дальше — stage, pre-prod и, наконец, деплой в prod. Месяц спустя обнаруживаем, что растёт прайс за облако. Оказывается, копятся диски. Эту ситуацию можно объяснить на примере пакетного менеджера Helm, который управляет релизами. Helm при удалении релиза удаляет только StatefulSet — при этом PVC и PV, которые создаются из него, копятся.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: psql
spec:
...
  volumeClaimTemplates:
  - metadata:
      name: pgdata
    spec:
      resources:
        requests:
          storage: 1Gi

Чтобы этого избежать, вместо volumeMount необходимо использовать Volume, смонтированный в отдельный PersistentVolumeClaim, в отдельный манифест. Тогда PVC будет содержаться в релизе и удаляться вместе с ревью-окружением.

apiVersion: apps/v1
kind: StatefulSet
Metadata:
  name: psql
spec:
    spec:
...
      volumes:
      - name: pgdata
        persistentVolumeClaim:
          claimName: pgdata
apiVersion: v1
kind: PersistentVolumeClaim
Metadata:
  name: pgdata
spec:
  resources:
    requests:
      storage: 1Gi
helm uninstall --debug psql-test -n test
uninstall.go:95: [debug] uninstall: Deleting psql-test
client.go:310: [debug] Starting delete for "psql-test" Service
client.go:310: [debug] Starting delete for "psql" StatefulSet
client.go:310: [debug] Starting delete for "pgdata" PersistentVolumeClaim

PVC удалилась. А вот PV остались.

kubectl get pv
NAME                                       CAPACITY RECLAIM POLICY   STATUS      
pvc-15d571eb-415a-4968-afb7-4f15cd0f5bef   2Gi      Retain           Released       
pvc-2660b9bd-65f0-4bf9-9857-4e036f6cb265   1Gi      Retain           Released      
pvc-2e428234-f7f6-448e-8913-cd74f9d3a451   1Gi      Retain           Released       
pvc-334ac272-47f1-4dcf-8fa9-f74b3c848874   2Gi      Retain           Released       
pvc-33c9cf2d-64a9-49ea-babe-1ac9c3045524   1Gi      Retain           Released       
pvc-3a93b9f6-866e-4947-b60f-09e75b1d899f   2Gi      Retain           Released

Они остались из-за reclaimPolicy. Это политика, которая наследуется из StorageClass, через который заказывали PersistentVolume. По умолчанию она имеет значение Delete.

kubectl get sc ssd -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd
...
reclaimPolicy: Delete

В этом случае PV удалится, если PVC освободил его. Однако в результате можно потерять важные данные.

Второе значение reclaimPolicy — Retain.

kubectl get sc ssd -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd
...
reclaimPolicy: Retain

При таком значении PV перейдёт в статус Released, и если удалить релиз с PVC, PV останутся в статусе Released. Более того, если затем вручную удалить эти PV из кластера, то в облаке они останутся. Это сделано для того, чтобы не потерять действительно важные данные.

На возможности потери данных остановимся чуть подробнее. Как мы помним, мы запустили Postgres в Kubernetes, и сейчас всё работает в prod«е. Тут оказывается, что разработчики нашли dev-стенд, решили, что им уже давно никто не пользуется и его можно удалить:

kubectl delete ns -n dev --all

Так они удалили не только свой dev-стенд, но и весь кластер — остались только kube-system и default. Смешная ситуация, но мы с ней неоднократно сталкивались. Самое время достать бэкапы, о которых говорили выше.

Если PV выпал в статус Released, его можно переиспользовать. PV помнит PVC, от которого был создан, поэтому нужно зачистить claimRef.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pvc-480c58d4-d871-4e68-b908-1f475a1b5f9c
 spec:
...
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: psql-pgdata-0
    namespace: production
  persistentVolumeReclaimPolicy: Retain
...

После такого патча PV переходит в статус Available, и его можно переиспользовать.

kubectl -n test get pv pv-test
NAME     CAPACITY RECLAIM POLICY  STATUS      CLAIM
pv-test   1Gi       Retain       Released  test/pgdata-psql-0
kubectl -n test patch pv pv-test -p
'{"spec":{"claimRef": null}}'
persistentvolume/pv-test patched
kubectl get pv get pv pv-test
NAME     CAPACITY RECLAIM POLICY   STATUS      CLAIM
pv-test   1Gi       Retain        Available

Если дальше создать PVC, который будет соответствовать по StorageClass и будет подходящего объёма, PV смонтируется.

kubectl -n test scale psql web --replicas 1
statefulset.apps/psql scaled
kubectl -n test get pv pv-test
NAME     CAPACITY  RECLAIM POLICY  STATUS      CLAIM
pv-test   1Gi       Retain         Bound  test/pgdata-psql-0

Если PVC будет меньше по объёму, в целом он будет удовлетворять требованиям и тоже смонтируется.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: psql
spec:
...
  volumeClaimTemplates:
  - metadata:
      name: pgdata
    spec:
      resources:
        requests:
          storage: 500Mi

Далее разберёмся с заменой StorageClass в StatefulSet.

Выше мы говорили про необходимость оценить производительность. К сожалению, не всё можно просчитать с самого начала — мы упираемся в диски. Придётся импровизировать. Переходим на SSD и просим администраторов создать новый StorageClass — нужно заменить его. Если попробовать изменить StorageClass прямо в манифесте StatefulSet, возникнет ошибка:

Error: UPGRADE FAILED: cannot patch "psql" with kind StatefulSet: StatefulSet.apps "psql" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden

Так происходит потому, что большинство полей в манифесте StatefulSet иммутабельные.

Чтобы всё-таки изменить StorageClass, можно удалить StatefulSet Postgres«а, который запущен в prod«е, с помощью опции --cascade=orphan.

kubectl -n test delete sts psql --cascade=orphan
statefulset.apps "psql" deleted

При этом поды, которые запущены из этого StatefulSet, останутся запущенными, с ними ничего не произойдёт.

Теперь нужно просто задеплоить новый StorageClass в StatefulSet. Когда StatefulSet масштабируется до нескольких новых реплик, новые поды начнут использовать новый StorageClass.

kubectl -n test scale sts psql --replicas 2
statefulset.apps/psql scaled
kubectl -n test get pvc
NAME    STATUS   VOLUME CAPACITY  STORAGECLASS  AGE
psql-0   Bound     pvc-1   1Gi        old         1h
psql-1   Bound     pvc-2   1Gi        new         5s

Важно понимать, что Kubernetes при этом не проводит никакую автомиграцию — нужно сделать это вручную либо приложение должно уметь это делать.

Отдельно поговорим про режим доступа к PersistentVolume.

3ddb1870c06cde73b6f9a949ec3bb3a8.png

Схема RWO, которую мы и описали выше, — самая обычная: есть потребитель, который читает и пишет в свой Volume. Это возможно в любых облаках, везде, где есть какой-то Storage.

ROX предполагает, что нужно читать из разных подов на разных узлах. Такое достижимо уже не везде.

RWX — самая изощрённая ситуация, когда нужно читать и писать из разных узлов в один Volume.

Матёрые сисадмины в такой ситуации посоветовали бы использовать NFS. Напомним: до этого мы строили отказоустойчивую инфраструктуру, поднимали кластер, возможно, думали про зону. А теперь нам предлагают поместить все persistent-данные в неотказоустойчивые NFS. Кроме того, NFS в этом году исполняется 40 лет — стоит уже отправить его на покой.

В манифесте The Twelve-Factor App от Heroku есть рекомендация использовать S3, если нужно где-то размещать статичные файлы, чтобы читать их и писать в них. Чтобы использовать такой подход, нужно применять какие-то новые библиотеки, переписать legacy-код (может быть, найти разработчиков, которые его писали). Не всегда это достижимо, поэтому приходится прибегать к поделкам вроде S3FS.

S3FS использует «под капотом» всем известный FUSE (Filesystem in Userspace). Соответственно, каждая операция чтения-записи — это три switch-контекста. Нормального IOPS (Input/Output Operations Per Second) здесь не получить: если положить так базу данных, можно упереться практически сразу. Поэтому для статики лучше использовать S3, для баз данных — хотя бы CephFS.

С ростом числа примитивов и опций приходится держать в голове всё больше информации, а значит, растёт риск ошибки. Чтобы его минимизировать, хочется всё автоматизировать. В этой связи рассмотрим ещё один пример. Представим, что нужно запустить кластер ClickHouse из двух реплик и одного шарда в Kubernetes. Необходимо со старта описать два StatefulSet для реплик. Будем кластеризировать по старинке через ZooKeeper. Казалось бы, чтобы всё это описать и связать между собой, потребуется много рутинной работы.

kubectl -n clickhouse-prod get sts
NAME      READY   
chi-0-0   1/1     
chi-0-1   1/1     
zookeeper 3/3 
kubectl -n clickhouse-prod get po
NAME       READY    STATUS    
chi-0-0-0   1/1     Running   
chi-0-1-0   1/1     Running   
zookeeper-0 1/1     Running   
zookeeper-1 1/1     Running   
zookeeper-2 1/1     Running

На самом деле это можно сделать с помощью одного YAML-файла: достаточно указать в нём, сколько реплик и шардов нужно, — дальше Kubernetes «автомагически» всё это задеплоит и развернёт.

apiVersion: clickhouse.altinity.com/v1
kind: ClickHouseInstallation
metadata:
  name: clickhouse
  namespace: clickhouse-prod
spec:
  configuration:
    clusters:
    - layout:
        replicasCount: 2
        shardsCount: 1
      name: production
...

07f674b4400009a0f46c8b38d62d8090.png

Такое возможно благодаря операторам. Они создают свои новые манифесты, из которых дальше создаются кастомные ресурсы (CR), которыми можно управлять.

Операторы — это, по сути, реализация различных способов управления софтом. Сегодня существует удобный фреймворк, который позволяет оценивать зрелость операторов.

1fe10bd0ee2e8982e4f6b54965e3f7a6.png

Операторы первого уровня позволяют создавать только базовые конфигурации для приложений. На втором уровне появились гладкие обновления, на третьем — управление всем жизненным циклом приложений с бэкапами и восстановлением после сбоев. Операторы четвёртого уровня предполагают комплексный мониторинг, который позволяет извлекать сложные метрики. Уровень «автопилот» означает, что мы только регулируем параметры, а все нужные изменения оператор вносит сам.

Далее поговорим про операторы, которыми чаще всего пользуются инженеры во «Фланте», и на их примере посмотрим, с какими подводными камнями можно столкнуться при использовании операторов.

Какие операторы мы используем и в чём их особенности

Для каждого оператора мы приведём количество звёзд на GitHub, нашу оценку уровня зрелости по фреймворку выше и количество открытых/закрытых issues для понимания, развивается ли оператор.

ClickHouse

Звёзды на GitHub: 1,6k

Зрелость: 3

Открытые/закрытые issues: 101/542

ClickHouse-оператор от Altinity мы используем давно и успешно. Он сам «из коробки» управляет процессами шардирования и масштабирования — достаточно задать количество реплик.

На примере этого оператора покажем, как оператор работает с кастомными ресурсами.

Чаще всего можно задеплоить только какой-то cluster-wide-оператор, который подписывается на все кастомные ресурсы в кластере. Поэтому, например, если в кластере есть и dev, и prod, нельзя обновить оператор только на dev и потестировать там его новую версию. Но если оператор может отслеживать конкретные пространства имён, тогда можно указать, что на пространство имён dev нужно установить новую версию. При этом старая продолжит существовать на prod«е. Последние версии Altinity такое умеют.

Вторая проблема вытекает из первой: иногда операторы подписываются на кастомные ресурсы и последовательно, один за другим, приводят их к требуемому виду, то есть образуется одна очередь. Поэтому, например, если сломалась инсталляция на dev«е, а на prod«е в это время закончилось место, вы добавляете кастомные ресурсы, увеличиваете диск. В целом, оператор может сам заказать в облаке диск большего объёма и сделать всё бесперебойно, но сейчас он наблюдает за упавшей dev-инсталляцией. Если у него один поток и он обрабатывает всё в одну очередь, ничего не произойдёт. Это решается деплоем двух операторов — по одному на каждое пространство имён.

Redis

Тут стоит сразу отметить, что практически все операторы не умеют строить Redis-кластер. Благо, нашим клиентам это обычно и не нужно — мы используем обычные Redis failover на Sentinel, и этого достаточно.

redis-operator by Spotahome

Звёзды на GitHub: 1,4k

Зрелость: 2

Открытые/закрытые issues: 5/324

Redis-оператор от Spotahome мы используем давно.

Стандартная ситуация: пришло много трафика — в результате закончился диск или RAM и реплика встала, нужно задеплоить её заново. Проблема этого оператора в хардкоде проб. Таким образом, реплика пытается задеплоиться и падает по пробе. Приходится выключить оператор (масштабировать его до нуля), задеплоить реплику и снова его включить. То есть нужен оператор оператора.

Поскольку Redis однопоточный, зачастую не хватает производительности. Поэтому мы часто просто в образе меняем бинарник Redis на KeyDB. Оператору от Spotahome без разницы, что он запускает, а значит, наш кастомный образ работает.

redis-operator by Opstree Solutions

Звёзды на GitHub: 622

Зрелость: 1

Открытые/закрытые issues: 58/327

Этот оператор, как видно, моложе предыдущего и уступает ему по уровню зрелости. Он здесь только потому, что однажды нам понадобилось создать мультиЦОД-инсталляцию Redis, а оператор от Spotahome не поддерживает TLS.

Kafka

Звёзды на GitHub: 4,3k

Зрелость: 3

Открытые/закрытые issues: 106/2248

Мы используем оператор от Strimzi — проекта инкубатора CNCF. Оператор неплохо написан, довольно зрелый, и его используют все сообщества CNCF.

Kafka изначально Cloud Native, то есть был разработан так, чтобы легко масштабироваться.

Из интересных особенностей: за сроком годности сертификатов нужно следить самостоятельно.

На примере этого оператора покажем, как разработчики добавляют дополнительные уровни абстракции, когда им не хватает стандартных примитивов Kubernetes.

Как мы знаем, поды в StatefulSet нумеруются от нуля до бесконечности. В Strimzi привязались к ID подов с помощью ID брокеров — это не позволяло удалять брокеры из середины. Поэтому они переписали примитив StatefulSet и создали свой StrimziPodSet. По сути, это набор подов, которые со своими ID брокеров позволяют динамически менять какие-то ресурсы. С одной стороны, это помогает и добавляет гибкости, с другой — даёт новую абстракцию. Когда падает узел, на котором запущен под, мы знаем, как поведёт себя StatefulSet: остановится, если диск локальный, или переедет в другое место. Поведение StrimziPodSet предсказать нельзя: документация не всегда помогает разобраться, поэтому приходится читать код.

PostgreSQL

Postgres operator by Zalando

Звёзды на GitHub: 3,8k

Зрелость: 3

Открытые/закрытые issues: 517/784

Вопрос развёртывания реляционных баз данных в Kubernetes изначально спорный, но мы давно и успешно делаем это с PostgreSQL.

Мы используем оператор от Zalando, в комплекте с которым идёт Patroni. Последний хорошо показывает себя как когда мы запускаем в кластере и сам Patroni, и PostgreSQL, так и когда используем PostgreSQL на виртуальных машинах или железе рядом с кластером, а в кластере работает только Patroni.

Стоит отметить отличный процесс in-place-обновлений. В образе конкретной версии оператора есть несколько идущих подряд версий компонента PostgreSQL. Это позволяет «на лету» обновить версию psql со всеми необходимыми миграциями, без перезапуска контейнеров. Однако версия PostgreSQL всегда связана с какой-то конкретной версией оператора. Поэтому, если используется оператор и выходит новая версия компонента, нельзя сразу же обновить её — надо дождаться поддержки новой версии в операторе. Также оператор может быть завязан на версию Kubernetes. Если у вас по каким-то причинам более старая версия, например из-за deprecated API, тоже придётся ждать.

При использовании этого оператора мы столкнулись с тем, что у нас повсеместно снимаются бэкапы PostgreSQL с помощью pg_basebackup. В операторе есть хардкод на восемь WAL«ов, и на высокопроизводительных базах данных мы просто не успевали делать бэкапы. Оказалось, раньше разработчики оператора использовали WAL-E, а в последнее время перешли на WAL-G. Мы разобрались, как всё устроено, и просто переделали свои бэкапы.

CloudNativePG

Звёзды на GitHub: 2,9k

Зрелость: 1

Открытые/закрытые issues: 197/1226

Мы помним историю с Elasticsearch от HashiCorp, поэтому, чтобы не зависеть от коммерческих проектов, смотрим на комьюнити-решения. Этот оператор моложе предыдущего, но явно развивается.

Сейчас он напрямую управляет подами, что плохо учитывается в автовосстановлении: когда под падает, приходится вмешиваться в работу оператора, потому что он неправильно восстанавливает, переключает на реплику.

MySQL

Звёзды на GitHub: 965

Зрелость: 2

Открытые/закрытые issues: 174/334

За последний год мы попробовали около 20 операторов, и большинство из них заточено на групповую репликацию, которая появилась в MySQL 8. Нам же нужно поддерживать MySQL более старых версий — 5.6 и 5.7, которые есть у клиентов. Более того, нужна простая master-slave-инсталляция, ведь довольно сложно объяснить клиенту, зачем ему покупать ещё один огромный сервер под базу данных.

Среди всех кандидатов мы выбрали для себя оператор от Bitpoke, которые когда-то создали Cloud Native WordPress. Недавно мы запустили с оператором первый prod — до этого он был только на dev.

У него есть «детские болячки»: например, он не умеет делать бэкапы бинлога, так как у него нет PITR. Мы используем обходные пути, чтобы решить эту проблему.

Подведём итоги по операторам. Из плюсов:

  • Операторы снижают порог входа.

  • В зависимости от уровня зрелости автоматизируют рутину и какие-то этапы жизненного цикла приложений.

  • Унифицируют подходы: если есть оператор, можно быть уверенным, что всё единообразно и не нужно беспокоиться о поддержке и обновлении «зоопарка» чартов компонента.

Главный минус — операторы добавляют уровни абстракции, с которыми приходится разбираться, а иногда и вовсе нужно анализировать код операторов, чтобы понять причину их того или иного поведения.

Стоит помнить, что именно пользователи делают операторы более зрелыми. Поэтому важно оставлять обратную связь на их работу.

Выводы

Прежде чем запускать Stateful-компонент в Kubernetes, стоит оценить возможности инфраструктуры и общую целесообразность. Не всегда размещение Stateful в Kubernetes — панацея или «серебряная пуля». Иногда имеет смысл использовать уже имеющиеся решения и наработанные практики. 

Запуск Stateful в Kubernetes требует применения уже известных подходов к построению инфраструктуры, однако имеет свою специфику и нюансы. Это стоит иметь в виду в начале проектирования подобного рода архитектур.

Чтобы автоматизировать те или иные этапы жизненного цикла Stateful, можно использовать операторы. Для решения одной задачи их можно найти как минимум несколько, а иногда — не один десяток. Может оказаться, что у оператора нет части нужной функциональности, поэтому придётся либо менять заранее выбранный оператор на новый, либо дорабатывать используемый самим. Так что важно изначально подобрать подходящий под нужды вариант, который требует минимум доработок.

В целом, сегодня в Kubernetes одинаково удобно управлять как Stateless-, так и Stateful-приложениями. Зачастую комфортность использования Stateful в приложении зависит от вендора, который предоставляет услуги Kubernetes, но если смотреть на картину в общем, Kubernetes эволюционирует и Stateful«у в Kubernetes — быть.

Видео и слайды

Видео с выступления (~49 минут):

Презентация доклада

P.S.

Читайте также в нашем блоге:

© Habrahabr.ru